<?php
namespace App\C5Client;

class HttpClient
{
    const PARAMETER_TYPE_NORMAL = "normal";
    const PARAMETER_TYPE_JSON = "json";
    const PARAMETER_TYPE_C5 = "c5";

    protected $headers = array();
    protected $parameters = array();
    protected $files = array();
    protected $debug = false;
    protected $baseUrl = "";
    protected $url = ""; // 调用的地址
    protected $parameterType = HttpClient::PARAMETER_TYPE_NORMAL;
    protected $result = "";
    protected $errorNo = "";
    protected $errorMessage = "";
    protected $method = "POST";
    protected $executeTimeoutSeconds = 300;
    protected $connectTimeoutSeconds = 120;

    public function call($method = "POST")
    {
        if (!empty($method)) {
            $this->method = $method;
        }
        $this->curl_call();
    }

    public function curl_call()
    {
        $this->debug("call url ", $this->getFullUrl());
        $this->debug("method", $this->method);
        $this->debug("header", $this->buildHeaders());

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $this->getFullUrl());
        curl_setopt($ch, CURLOPT_HTTPHEADER, $this->buildHeaders());
        // curl_setopt($ch, CURLOPT_REFERER, "http://www.163.com/ ");   //构造来路
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch, CURLOPT_TIMEOUT, $this->executeTimeoutSeconds);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->connectTimeoutSeconds);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $this->method);
        //  curl_setopt($ch, CURLOPT_UPLOAD, empty($this->files));
        curl_setopt($ch, CURLOPT_BINARYTRANSFER, !empty($this->files));

        if (empty($this->files) && $this->parameterType == HttpClient::PARAMETER_TYPE_JSON) {
            $data_string = $this->getPostData();
            curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
            $this->debug("post data", $data_string);
        } else if (empty($this->files)) {
            curl_setopt($ch, CURLOPT_POSTFIELDS, $this->getPostData());
            $this->debug("post data", $this->getPostData());
        } else {
            // has file
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $this->getPostData());
            $this->debug("post with file ", $this->getPostData());
        }

        $string = curl_exec($ch);
        $this->debug("result raw info", $string);

        if (curl_errno($ch)) {    //出错则显示错误信息
            $this->errorNo = curl_errno($ch);
            $this->errorMessage = curl_error($ch);
            $this->debug("curl errors", $this->errorMessage);
        }
        curl_close($ch);
        if (ord($string[0]) == 239 && ord($string[1]) == 187 && ord($string[2]) == 191) {
            $string = substr($string, 3);
        }
        $this->result = $string;
        $this->debug("final result", $this->result);
    }

    public function getPostData()
    {
        if (empty($this->files) && $this->parameterType == HttpClient::PARAMETER_TYPE_JSON) {
            return json_encode($this->parameters);
        }
        if (empty($this->files)) {
            return http_build_query($this->parameters);
        }
        $args = array_merge(array(), $this->parameters);
        foreach ($this->files as $file) {
            $args[$file["post_name"]] = new CURLFile($file["file"], $this->getFileMimeType($file["file"]), $file["file_name"]);
        }
        return $args;
    }

    public function getFileMimeType($file)
    {
        if (function_exists("finfo_open")) {
            $info = finfo_open(FILEINFO_MIME_TYPE);
            return finfo_file($info, $file);
        } else {
            return getMimeType($file);
        }
    }

    public function buildHeaders()
    {
        $this->initContentType();
        $list = array();
        foreach ($this->headers as $key => $value) {
            if (empty($value)) {
                continue;
            }
            array_push($list, "$key: $value");
        }
        return $list;
    }

    public function getFullUrl()
    {
        $url = $this->url;
        if (!$this->startsWith(strtolower($url), strtolower($this->baseUrl))) {
            return $this->baseUrl . $url;
        }
        return $url;
    }

    public function get()
    {
        $this->call("GET");
    }

    public function post()
    {
        $this->call("POST");
    }

    public function delete()
    {
        $this->call("DELETE");
    }

    public function put()
    {
        $this->call("PUT");
    }

    public function getResult()
    {
        return $this->result;
    }

    public function getResultArray()
    {
        if (is_array($this->result)) {
            return $this->result;
        }
        return json_decode($this->result, true);
    }

    public function buildContent()
    {
        if ($this->parameterType == HttpClient::PARAMETER_TYPE_JSON) {
            return json_encode($this->parameters);
        } else {
            return http_build_query($this->parameters);
        }

    }

    public function initContentType()
    {
        if (!empty($this->files)) {
            $this->add_header("Content-type", "multipart/form-data");
            return;
        }
        if ($this->parameterType == HttpClient::PARAMETER_TYPE_JSON) {
            $this->add_header("Content-type", "application/json");
        } else {
            $this->add_header("Content-type", "application/x-www-form-urlencoded");
        }
    }

    public function get_milli_time()
    {
        $time_test = microtime();
        list($ten_thousand, $sec) = explode(" ", $time_test);
        $time = ((float)$ten_thousand + (float)$sec);
        return round($time * 1000);
    }

    public function debug($tag, $target)
    {
        if ($this->debug) {
            $info = $target;
            if (is_array($target) || is_object($target)) {
                $info = json_encode($target);
            }
            print "<br>c5_client debug: $tag : " . $info;
        }
    }

    public function add_parameter($name, $value)
    {
        $this->parameters[$name] = $value;
    }

    public function addParameter($name, $value)
    {
        $this->add_parameter($name, $value);
    }

    public function addParameters($parameters)
    {
        $this->add_parameters($parameters);
    }

    public function add_parameters($parameters)
    {
        $this->parameters = array_merge($this->parameters, $parameters);
    }


    public function add_header($name, $value)
    {
        $this->headers[$name] = $value;
    }

    public function add_headers($headers)
    {
        $this->headers = array_merge($this->headers, $headers);
    }

    public function addHeaders($headers)
    {
        $this->add_headers($headers);
    }

    public function addHeader($name, $value)
    {
        $this->add_header($name, $value);
    }

    /**
     * @return array
     */
    public function getHeaders()
    {
        return $this->headers;
    }

    /**
     * @param array $headers
     */
    public function setHeaders($headers)
    {
        $this->headers = $headers;
    }

    /**
     * @return array
     */
    public function getParameters()
    {
        return $this->parameters;
    }

    /**
     * @param array $parameters
     */
    public function setParameters($parameters)
    {
        $this->parameters = $parameters;
    }

    /**
     * @return int
     */
    public function getTimeout()
    {
        return $this->timeout;
    }

    /**
     * @param int $timeout
     */
    public function setTimeout($timeout)
    {
        $this->timeout = $timeout;
    }

    /**
     * @return boolean
     */
    public function isDebug()
    {
        return $this->debug;
    }

    /**
     * @param boolean $debug
     */
    public function setDebug($debug)
    {
        $this->debug = $debug;
    }

    /**
     * @return string
     */
    public function getUrl()
    {
        return $this->url;
    }

    /**
     * @param string $url
     */
    public function setUrl($url)
    {
        $this->url = $url;
    }

    /**
     * @return string
     */
    public function getBaseUrl()
    {
        return $this->baseUrl;
    }

    /**
     * @param string $baseUrl
     */
    public function setBaseUrl($baseUrl)
    {
        $this->baseUrl = $baseUrl;
    }

    /**
     * @return array
     */
    public function getFiles()
    {
        return $this->files;
    }

    /**
     * @param array $files
     */
    public function setFiles($files)
    {
        foreach ($files as $file) {
            if (is_string($file)) {
                $this->addFile($file);
                return;
            }
            $this->files[] = $file;
        }
    }

    public function addFile($file, $file_name = "", $post_name = "")
    {
        if (empty($file_name)) {
            $file_name = basename($file);
        }
        if (empty($post_name)) {
            $post_name = basename($file);
        }
        $this->files[] = array("file_name" => $file_name, "file" => $file, "post_name" => $post_name);
    }

    /**
     * @return string
     */
    public function getParameterType()
    {
        return $this->parameterType;
    }

    /**
     * @param string $parameterType
     */
    public function setParameterType($parameterType)
    {
        $this->parameterType = $parameterType;
    }

    public function setJsonParameterType()
    {
        $this->setParameterType(HttpClient::PARAMETER_TYPE_JSON);
    }

    /**
     * @return string
     */
    public function getMethod()
    {
        return $this->method;
    }

    /**
     * @param string $method
     */
    public function setMethod($method)
    {
        $this->method = $method;
    }


    public function startsWith($string, $prefix)
    {
        if (strlen($string) < strlen($prefix)) {
            return false;
        }
        $len = strlen($prefix);
        return substr($string, 1, $len) == $prefix;
    }


}
